<?php
/**
 * Авторизация пользователя.
 *
 * @property Zend_Controller_Request_Http $_request
 */
class Users_LoginController extends Users_Model_Abstract_ClientController {
	const FORM_CONFIG_LOGIN = 'forms/login';
	const FORM_CONFIG_REGISTER = 'forms/register';
	const FORM_CONFIG_RESTORE_PASSWORD = 'forms/restore-password';

	// Общая инициализация.
	function init() {
		parent::init();
    }

	/**
	 * Основное действие по авторизации или отображению соответствующей формы.
     * Проверка того, что пользователь авторизован: Workset_Auth::getInstance()->hasIdentity()
	 */
	function indexAction() {
		if (!Users_Model_User::getUser()->isGuest() || $this->_request->getParam('cancel')) {
			$this->_redirectToBackUrl();
		}

		// Создание объекта формы.
        /** @noinspection PhpUndefinedMethodInspection */
		$aFormConfig = $this->_helper->moduleConfig(self::FORM_CONFIG_LOGIN);
		$oForm = new Users_Form_Login($aFormConfig);
		$oForm->setTranslator($this->_translate);
		$oForm->addActionParams(array(
			'backurl' => $this->_request->getParam('backurl',$this->_request->getHeader('referer')),
		));

		// В случае наличия POST данных, начинаем их обработку.
		if ($this->_request->isPost()) {
			// Если есть поле "пароль пользователя", то проводим стандартную аутентификацию.
			if ($this->_request->getPost('user_password','')) {
				$aPost = $this->_request->getPost();

				// Если данные валидны, то пытаемся провести авторизацию.
				if ($oForm->isValid($aPost)) {
					$aFields = $oForm->getValues();
					$sLogin = $aFields['user_login'];
					$sPassword = $aFields['user_password'];
					$bRemember = $aFields['user_remember'] == 1;

					// Проверка аутентификации.
                    /** @noinspection PhpUndefinedMethodInspection */
					if (!Zend_Registry::get('auth')->login($sLogin,$sPassword,$bRemember)) {
						// Пользователь ввёл некорректные логин и пароль.
						$oForm->addError('Вы ввели неправильные логин или пароль!');
					}
				}
			}
			// ...если нет поля "пароль пользователя", то проводим аутентификацию по OpenId.
			else {
				$sLogin = $this->_request->getPost('user_login','');

				// Проверка аутентификации.
                /** @noinspection PhpUndefinedMethodInspection */
				if (!Zend_Registry::get('auth')->login($sLogin,null,false)) {
					// Пользователь ввёл некорректные логин и пароль.
					$oForm->addError('Вы ввели неправильный OpenID.');
				}
			}
		}

		// Аутентификация прошла успешно. Проверяем куда перенаправить пользователя.
        /** @noinspection PhpUndefinedMethodInspection */
		if (Zend_Registry::get('auth')->hasIdentity()) {
			$this->_redirectToBackUrl();
		}

		/** @var Workset_Controller_Action_Helper_Url $helperUrl */
		$helperUrl = $this->_helper->getHelper('Url');
		// Запоминаем объект формы для вывода в шаблоне вида.
        /** @noinspection PhpUndefinedFieldInspection */
        $this->view->form = $oForm;
		$this->view->msg = $this->_translate;
		$this->view->urlRestorePassword = $helperUrl->simple('restore-password', 'login', 'users', array(
			'backurl' => $this->_request->getParam('backurl'),
		));
	}


	/**
	 * Обработка ответа от OpenId провайдера.
	 */
	public function openidAction() {
		Workset_Auth::getInstance()->login(false,false,false);

		// Аутентификация прошла успешно. Проверяем куда перенаправить пользователя.
        /** @var Workset_Auth $auth */
		if (($auth = Zend_Registry::get('auth')) && $auth->hasIdentity()) {
			$this->_redirectToBackUrl();
		}
		else {
			$this->indexAction();
		}
	}

	/**
	 * Сброс флагов аутентификации и запоминания пользователя.
	 */
	public function exitAction() {
		if (!Users_Model_User::getUser()->isGuest()) {
			session_destroy();
			setcookie('remember',false,time() - 1,'/');
		}

		$this->_redirectToBackUrl();
	}

	/**
	 * Форма регистрации нового пользователя.
	 */
	public function registerAction() {
		if (!Users_Model_User::getUser()->isGuest() || $this->_request->getParam('cancel')) {
			$this->_redirectToBackUrl();
		}

		// Создание объекта формы.
        /** @noinspection PhpUndefinedMethodInspection */
		$aFormConfig = $this->_helper->moduleConfig(self::FORM_CONFIG_REGISTER);
		$oForm = new Users_Form_Login($aFormConfig);
		$oForm->setTranslator($this->_translate);

		// В случае наличия POST данных, начинаем их обработку.
		if ($this->_request->isPost()) {
			$aPost = $this->_request->getPost();

			// Если данные валидны, то регистрируем пользователя.
			if ($oForm->isValid($aPost)) {
				$sEmail = $oForm->getValue('user_email');
				$sLogin = $oForm->getValue('user_login');
				$sPassword = $oForm->getValue('user_password');
				$sPasswordHash = Workset_Auth::getInstance()->getPasswordHash($sLogin,$sPassword);
				$sTempPassword = 'Temporary password: '.microtime();
				$sTempPasswordHash = Workset_Auth::getInstance()->getPasswordHash($sLogin,$sTempPassword);

				$oUser = Users_Model_User_Db::addUser(array(
					'login' => $sLogin,
					'password' => $sTempPasswordHash,
					'email' => $sEmail,
				));

				// Если аутентификация нового пользователя прошла успешно, то...
                /** @noinspection PhpUndefinedMethodInspection */
				if (Zend_Registry::get('auth')->login($sLogin,$sTempPassword,false)) {
					// Формируем письмо со ссылкой для активации.
					$sUrl = $this->_getActivateUrl($oUser,$sPasswordHash);
					$sHtml = 'Пожалуйста, для активации аккаунта перейдите по ссылке <a href="'.$sUrl.'">'.$sUrl.'</a>';

					// Отправляем письмо со ссылкой для активации.
					$oMail = new Zend_Mail('utf-8');
					$oMail->setHeaderEncoding(Zend_Mime::ENCODING_BASE64);
					$oMail->addTo($sEmail,$sLogin);
					$oMail->setSubject('Активация аккаунта на сайте '.$this->_request->getHttpHost());
					$oMail->setBodyHtml($sHtml,'utf-8',Zend_Mime::ENCODING_QUOTEDPRINTABLE);
					$oMail->send();

					// Делаем редирект на нужную нам страницу.
					$this->_redirectToBackUrl();
				}
			}
		}

		// Запоминаем объект формы для вывода в шаблоне вида.
        /** @noinspection PhpUndefinedFieldInspection */
        $this->view->form = $oForm;
		$this->view->msg = $this->_translate;
	}

	/**
	 * Активизация аккаунта. Заключается в присвоении ему правильного пароля.
	 */
	public function activateAction() {
		$aPNameList = array('module','controller','action','login','data','sign','backurl');
		$aParams = $this->_request->getParams();
		$aPKeys = array_keys($aParams);

		// Проверка корректности входных параметров.
		switch (TRUE) {
			// Проверка количества входных параметров и наличия необходимых параметров.
			case count(array_diff($aPNameList,$aPKeys)) || count(array_diff($aPKeys,$aPNameList)):
				throw new Zend_Exception('Wrong request params.');

			// Проверка существования пользователя с данным логином.
			case !$oUser = Users_Model_User_Db::getByLogin($aParams['login']):
				throw new Zend_Exception('Wrong user login.');

			// Проверка цифровой подписи.
			case $aParams['sign'] !== md5(join(':',array($oUser->getSecretWord(),$aParams['login'],$aParams['data']))):
				throw new Zend_Exception('Wrong signature.');
		}

		// Применение нового пароля.
		$sPasswordHash = $this->_xorHash($oUser->getPasswordHash(),$aParams['data']);
		$oUser->setPasswordHash($sPasswordHash);
		$oUser->save();

		// Делаем редирект на нужную нам страницу.
		$this->_redirectToBackUrl();
	}

	/**
	 * Метод восстановления пароля.
	 */
	public function restorePasswordAction() {
		// Создание объекта формы.
        /** @noinspection PhpUndefinedMethodInspection */
		$aFormConfig = $this->_helper->moduleConfig(self::FORM_CONFIG_RESTORE_PASSWORD);
		$oForm = new Users_Form_Login($aFormConfig);
		$oForm->setTranslator($this->_translate);

		// В случае наличия POST данных, начинаем их обработку.
		if ($this->_request->isPost()) {
			$aPost = $this->_request->getPost();

			// Если данные валидны, то создаём и отправляем письмо со ссылкой для применения нового пароля.
			if ($oForm->isValid($aPost)) {
				$oUser = Users_Model_User::getByEMail($sEmail = $oForm->getValue('user_email'));
				$sLogin = $oUser->getLogin();
				$sPassword = $oForm->getValue('user_password');
				$sPasswordHash = Workset_Auth::getInstance()->getPasswordHash($sLogin,$sPassword);

				// Формируем письмо со ссылкой для активации.
				$sUrl = $this->_getActivateUrl($oUser,$sPasswordHash);
				$sHtml = 'Пожалуйста, для активации нового пароля перейдите по ссылке <a href="'.htmlspecialchars($sUrl).'"> '.$sUrl.' </a>';

				// Отправляем письмо со ссылкой для активации.
				$oMail = new Zend_Mail('utf-8');
				$oMail->setHeaderEncoding(Zend_Mime::ENCODING_BASE64);
				$oMail->addTo($sEmail, $sLogin);
				$oMail->setSubject('Активация нового пароля для сайта '.$this->_request->getHttpHost());
				$oMail->setBodyHtml($sHtml, 'utf-8', Zend_Mime::ENCODING_QUOTEDPRINTABLE);
				$oMail->send();

				// Делаем редирект на нужную нам страницу.
				$this->_redirectToBackUrl();
			}
		}

		// Запоминаем объект формы для вывода в шаблоне вида.
        /** @noinspection PhpUndefinedFieldInspection */
		$this->view->form = $oForm;
		$this->view->msg = $this->_translate;
	}

	/**
	 * Перенаправление пользователя по обратному УРЛ.
	 */
	protected function _redirectToBackUrl() {
        /** @noinspection PhpAssignmentInConditionInspection */
        if ($sBackUrl = $this->_request->getParam('backurl')) {
            /** @noinspection PhpUndefinedMethodInspection */
			$sBackUrl = $this->_helper->httpUri($sBackUrl);
		}
		else {
			$sBackUrl = strpos($_SERVER['HTTP_REFERER'],parse_url('http://'.$this->_request->getHttpHost().$_SERVER['REQUEST_URI'],PHP_URL_PATH)) >= 0
				? 'http://'.$this->_request->getHttpHost().'/'
				: $_SERVER['HTTP_REFERER'];
		}

		$this->_response->setRedirect($sBackUrl,302);
	}

	/**
	 * Проведение операции XOR для двух хешей.
	 * 
	 * @param string $sHash1
	 * @param string $sHash2
	 * @return string
	 */
	protected function _xorHash($sHash1,$sHash2) {
		$sHash = '';
		$aHex = array(
			0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f',
			'a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15,
			'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
		);

		for ($i=0, $iLen=min(strlen($sHash1),strlen($sHash2)); $i<$iLen; $i++) {
			if (isset($aHex[$sHash1[$i]]) && isset($aHex[$sHash2[$i]])) {
				$sHash.= (string)$aHex[$aHex[$sHash1[$i]] ^ $aHex[$sHash2[$i]]];
			}
			else {
				$sHash.= '?';
			}
		}

		return $sHash;
	}

	/**
	 * Получение URL активации пароля.
	 * 
	 * @param Users_Model_User $oUser
	 * @param string $sPasswordHash
	 * @return string
	 */
	protected function _getActivateUrl(Users_Model_User $oUser,$sPasswordHash) {
		$sRestoreHash = $this->_xorHash($sPasswordHash,$oUser->getPasswordHash());
		$aSign = array($oUser->getSecretWord(),$oUser->getLogin(),$sRestoreHash);
		$aParams = $this->_request->getParams();
		$sQuery = http_build_query(array(
			'login' => $oUser->getLogin(),
			'data' => $sRestoreHash,
			'sign' => md5(join(':',$aSign)),
			'backurl' => !isset($aParams['backurl'])
				? "/$aParams[module]/profile/index/edit?backurl=".urlencode('/')
				: $aParams['backurl'],
		),'','&');
		$sHost = $this->_request->getHttpHost();

		return "http://$sHost/$aParams[module]/$aParams[controller]/activate?$sQuery";
	}
}
?>